package lion.framework.core.task.rule;

import java.util.Calendar;
import java.util.Date;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.apache.commons.lang.StringUtils;

import lion.dev.lang.MapJ;
import lion.dev.text.Formater;
import lion.dev.web.Validator;
import lion.framework.core.task.anno.TimerRule;

/**
 * 默认时间触发规则,该规则是时间和运行间隔的组合， 配置参数：<br/>
 * start:默认为当前时间<br/>
 * end:<br/>
 * interval:默认为1秒，可以指定单位min,h或d, 1min为1分钟，1h为1小时 ,1d为1天 <br/>
 * 支持的时间格式有：
 * 
 * <pre>
 * yyyy-MM-dd HH:mm:ss 指定年月日，时分秒的固定时间
 * HH:mm:ss  循环每日的固定的时，分，秒时间，
 * mm:ss     循环每小时的固定的分和秒时间
 * </pre>
 * 
 * <pre>
 * time   2013 2013年12月13日 下午4:07:44
 * mail   hl_0074@sina.com
 * </pre>
 * 
 * @author lion
 */
@TimerRule("default")
public class DefaultTimerRule extends AbstractTimerRule {

	private String startTimeStr;
	private String endTimeStr;

	private Date startTime = null;
	private Date endTime = null;
	private int interval = 1;

	public DefaultTimerRule(MapJ config) {

		super(config);
		this.startTimeStr = config.getString("start");
		this.endTimeStr = config.getString("end");
		String intv = config.getString("interval");

		this.interval = 1;
		if (StringUtils.isNotBlank(intv)) {
			Pattern p = Pattern.compile("(\\d+)([a-z]+)");
			Matcher m = p.matcher(intv);
			if (m.matches()) {
				String num = m.group(1);
				String unit = m.group(2);

				int val = Validator.toInt(num, -1);
				if (val < 0) { return; }
				if ("min".equals(unit)) {
					val *= 60;
				} else if ("h".equals(unit)) {
					val *= 60 * 60;
				} else if ("d".equals(unit)) {
					val *= 60 * 60 * 24;
				}
				this.interval = val;
			} else {
				this.interval = Validator.toInt(intv, 1);
			}
		}

	}

	@Override
	public boolean checkRunTime(Date last) {

		// trim millisecond
		Calendar c = Calendar.getInstance();
		c.set(Calendar.MILLISECOND, 0);
		Date now = c.getTime();
		// check start time
		if (this.getStartTime() != null && now.getTime() < this.getStartTime().getTime()) { return false; }

		// check end time
		if (this.getEndTime() != null && now.getTime() > this.getEndTime().getTime()) { return false; }

		// check is time to run
		Date next = this.getNextRunTime(last);
		if (next == null) { return false; }

		if (now.getTime() >= next.getTime()) { return true; }

		return false;
	}

	public Date getNextRunTime(Date lastRuntime) {

		Date start = getStartTime();
		Calendar c = Calendar.getInstance();
		c.set(Calendar.MILLISECOND, 0);
		long now = c.getTimeInMillis();
		if (lastRuntime == null) {
			// before start time,use the start time
			if (now <= start.getTime()) { return start; }
			// never ran before,set last run time as the start time
			c.setTime(start);
		} else {
			c.setTime(lastRuntime);
		}
		c.set(Calendar.MILLISECOND, 0);
		// get next time point to run which close to current time
		while (c.getTimeInMillis() < now) {
			c.add(Calendar.SECOND, this.interval > 0 ? this.interval : 1);
		}
		Date result = c.getTime();
		Date endTime = getEndTime();
		// expired,no next time to run
		if (endTime != null && result.getTime() > endTime.getTime()) { return null; }

		return result;
	}

	public Date getStartTime() {

		if (this.startTime == null && StringUtils.isBlank(this.startTimeStr)) {
			Calendar c = Calendar.getInstance();
			c.set(Calendar.MILLISECOND, 0);
			this.startTime = c.getTime();
			return startTime;
		}
		if (this.startTime != null && StringUtils.isBlank(this.startTimeStr)) { return startTime; }

		if (StringUtils.isNotBlank(this.startTimeStr)) {
			this.startTime = parseTime(this.startTimeStr);
		}
		return this.startTime;
	}

	public Date getEndTime() {

		if (this.endTime == null && StringUtils.isBlank(this.endTimeStr)) { return null; }
		if (this.endTime != null && StringUtils.isBlank(this.endTimeStr)) { return this.endTime; }

		if (StringUtils.isNotBlank(this.endTimeStr)) {
			this.endTime = parseTime(this.endTimeStr);
		}
		return this.endTime;
	}

	private static Date parseTime(String str) {

		if (StringUtils.isBlank(str) || str.indexOf(":") < 0) { return null; }
		Date date = Formater.parseDate("yyyy-MM-dd HH:mm:ss", str);
		if (date == null) {
			Calendar c = Calendar.getInstance();
			c.set(Calendar.MILLISECOND, 0);
			String[] arr = str.split(":");
			int hour = -1, minute = -1, second = -1, i = 0;
			if (arr.length == 3) {
				hour = Validator.toInt(arr[i++], -1);
			}
			minute = Validator.toInt(arr[i++], -1);
			second = Validator.toInt(arr[i], -1);

			if (hour > -1 && hour < 24) {
				c.set(Calendar.HOUR_OF_DAY, hour);
			}
			if (minute > -1 && minute < 60) {
				c.set(Calendar.MINUTE, minute);
			}
			if (second > -1 && second < 60) {
				c.set(Calendar.SECOND, second);
			}
			c.set(Calendar.MILLISECOND, 0);
			date = c.getTime();
		}
		return date;
	}

	public void setEndTime(Date endTime) {

		this.endTime = endTime;
	}

	public void setStartTime(Date startTime) {

		this.startTime = startTime;
	}

	public int getInterval() {

		return interval;
	}

	public void setInterval(int interval) {

		this.interval = interval;
	}

	@Override
	public boolean isExpired() {

		if (StringUtils.isNotBlank(this.endTimeStr) && this.endTimeStr.indexOf("-") > 0) {
			Date now = new Date();
			Date end = getEndTime();
			if (now.getTime() >= end.getTime()) { return true; }
		}

		return false;
	}
}
